package com.br4mmie.asciistory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

import com.br4mmie.asciistory.battle.BattleReward;
import com.br4mmie.asciistory.battle.BattleWindow;
import com.br4mmie.asciistory.characters.NPC;
import com.br4mmie.asciistory.characters.Player;
import com.br4mmie.asciistory.graphics.WorldRenderer;
import com.br4mmie.asciistory.map.*;
import com.br4mmie.asciistory.mission.Item;
import com.br4mmie.asciistory.mission.ItemMapObject;
import com.br4mmie.asciistory.mission.Mission;
import com.br4mmie.asciistory.ui.*;

public class GameLoop 
{
	private WindowHandler 	windows;
	
	private CharImage 		gameScreen;
	private CharImage		interfaceScreen;
	private CharImageDrawer interfaceDrawer;
	
	private int 			screenWidth, 
							screenHeight;
	private Camera 			camera;
	private Map 			map;
	private Player 			player;
	private PlayerAvatar 	avatar;
	private ProfileManager	manager;
	private WorldRenderer	renderer;
	
	public GameLoop(ProfileManager manager, CharImageDrawer ui)
	{
		this.manager = manager;
		this.interfaceDrawer = ui;
	}
	
	public void setupMainMenu()
	{
		OptionWindow.SelectionListener menuListener = new OptionWindow.SelectionListener(){

			
			public void select(int option) 
			{
				switch(option){
				
				case 1 : manager.requestProfile(false); return;
				case 2 : startTutorial("<tutorial>"); return;
				case 3 : manager.requestProfile(true); return;
				case 4 : ASCIIStoryActivity.setRunning(false);
				}
				
			}
						
		};
		OptionWindow menuWindow = new OptionWindow(0, 0, getScreenWidth(), getScreenHeight(), "Menu", menuListener);
		menuWindow.addOption("New Game");
		menuWindow.addOption("Play Tutorial");
		menuWindow.addOption("Load Game");
		menuWindow.addOption("Exit");
	}
	
	public void addWindow(Window window)
	{
		windows.add(window);
	}
	
	public void startUp()
	{
		windows = new WindowHandler();
		
		startNewGame("null"); //just to have a game..
		
		screenWidth = 60;
		screenHeight = 30;
		
		gameScreen 		= new CharImage(screenWidth, screenHeight);
		interfaceScreen = new CharImage(screenWidth, screenHeight);
		
		setupMainMenu();
	}
	
	public void startTutorial(String profilename)
	{
		manager.setProfilename(profilename);
		
		try 
		{
			MapLoader mapLoader = new MapLoader("Data/Maps/Tutorial.map");
			map = mapLoader.createMap();
		}
		catch (NumberFormatException e){}
		catch (IOException ex) {ex.printStackTrace();}
		
		camera = new Camera(5, 5);
		
		player = new Player();
		avatar = new PlayerAvatar(map);
		avatar.setX(map.getSpawnX());
		avatar.setY(map.getSpawnY());
		map.addMapObject(avatar);
		camera.lock(avatar);
		camera.setOffsetX(-screenWidth/2);
		camera.setOffsetY(-screenHeight/2);
		
		if (renderer != null) renderer.setMap(map.getImage().toString());
	}
	private void deleteFile(String filename)
	{
		File f = new File(filename);
		f.delete();
	}
	public void startNewGame(String profilename)
	{
		manager.setProfilename(profilename);
		deleteFile("Saves/"+profilename+".root.savegame");
		deleteFile("Saves/"+profilename+".map1.map.savegame");
		deleteFile("Saves/"+profilename+".map2.map.savegame");
		deleteFile("Saves/"+profilename+".inn.map.savegame");
		deleteFile("Saves/"+profilename+".Tutorial.map.savegame");
		deleteFile("Saves/"+profilename+".FailsomeForrest.map.savegame");
		deleteFile("Saves/"+profilename+".Cave.map.savegame");
		deleteFile("Saves/"+profilename+".DauwOTwylaertsShop.map.savegame");
		
		try 
		{
			MapLoader mapLoader = new MapLoader("Data/Maps/map1.map");
			map = mapLoader.createMap();
		}
		catch (NumberFormatException e){}
		catch (IOException ex) {ex.printStackTrace();}
		
		camera = new Camera(5, 5);
		
		player = new Player();
		avatar = new PlayerAvatar(map);
		avatar.setX(map.getSpawnX());
		avatar.setY(map.getSpawnY());
		map.addMapObject(avatar);
		camera.lock(avatar);
		camera.setOffsetX(-screenWidth/2);
		camera.setOffsetY(-screenHeight/2);
		
		if (renderer != null) renderer.setMap(map.getImage().toString());
	}
	
	private void handlePortals(Portal p)
	{
		try 
		{
			manager.saveGame(manager.getProfileName() + "." + map.getFilename() + ".savegame", false);
			
			MapLoader ml = new MapLoader(p.getTarget(), p.getTargetX(), p.getTargetY());
			map = ml.createMap();
			avatar.setX(map.getSpawnX());
			avatar.setY(map.getSpawnY());
			
			try 
			{
				manager.loadGame(manager.getProfileName() + "." + map.getFilename() + ".savegame", false);
			} catch (FileNotFoundException e) {
				System.out.println("We haven't been on this map yet. Ignoring FileNotFoundException");
			}
		
			avatar.setMap(map);
			
			camera.setX(0);
			camera.setY(0);
			
			map.addMapObject(avatar);
			renderer.setMap(map.getImage().toString());
			
			//autosave
			manager.saveGame(manager.getProfileName() + ".root.savegame", true);
			
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private void pickup()
	{
		List<MapObject> objects = map.getObjectsAt(avatar.getX(), avatar.getY());
		for (MapObject mo: objects)
		{
			if (mo instanceof ItemMapObject)
			{
				//add the item to the inventory
				player.getInventory().receiveItem(((ItemMapObject)mo).getItem());
				
				//remove item from map
				map.removeMapObject(mo);
			}
			if (mo instanceof Portal)
			{
				handlePortals((Portal)mo);
			}
		}
	}
	private void updateMissions(boolean talk)
	{
		try
		{
			for (Mission m: player.getMissions())
			{
				m.handleObjectives(player.getMissions(), player, talk);
			}
		}
		catch (Exception ex) {ex.printStackTrace();}
	}
	private void talkNPC(int x, int y)
	{
		List<MapObject> objects = map.getObjectsAt(x, y);
		for (MapObject mo: objects)
		{
			if (mo instanceof NPC)
			{
				updateMissions(false);
				((NPC)mo).talkToNPC(player);
				((NPC)mo).updateMission(player);
				
				Mission mission = ((NPC)mo).getMission();
				if (mission != null)
				{
					boolean ignore = false;
					for (Mission m: player.getMissions())
					{
						if (m.getName().equals(mission.getName())) ignore = true;
					}
					if (!ignore)
					{
						player.getMissions().add(mission);
						mission.setAdded(true);
					}
				}
				
				updateMissions(false);
			}
		}
	}
	private void talkNPC()
	{
		int x = avatar.getX();
		int y = avatar.getY();
		talkNPC(x-1, y);
		talkNPC(x+1, y);
		talkNPC(x, y-1);
		talkNPC(x, y+1);
	}

	public void handleEvents()
	{
		if(windows.handleEvents())return;
		try
		{
			if(Keybindings.LEFT.pressed())		
				if(!checkNPCcollision(avatar.getX()-1,avatar.getY()))
					{avatar.move(-1, 0);map.encounter();}
					
			if(Keybindings.UP.pressed())		
				if(!checkNPCcollision(avatar.getX(),avatar.getY()-1))
					{avatar.move(0, -1);map.encounter();}
					
			if(Keybindings.RIGHT.pressed())		
				if(!checkNPCcollision(avatar.getX()+1,avatar.getY()))
					{avatar.move(1, 0);map.encounter();}
					
			if(Keybindings.DOWN.pressed())		
				if(!checkNPCcollision(avatar.getX(),avatar.getY()+1))
					{avatar.move(0, 1);map.encounter();}
					
			if(Keybindings.INTERACT.pressed()) 	{pickup(); talkNPC();}
			if(Keybindings.INVENTORY.pressed()) {player.getInventory().showInventoryWindow();}
			if(Keybindings.INV_EQP.pressed())   {player.getInventory().showEquipWindow();}
			if(Keybindings.JOURNAL.pressed())	{new Journal(player.getMissions()).activate();}
			if(Keybindings.STATUS.pressed())    {new PlayerStatus();}
			
			updateMissions(false);
			
			/*if(KeyPoller.getAsyncKeyState(0x70)) 
			{
				TextWindow testWindow2 = new TextWindow(0, 0, screenWidth, screenHeight, 1, false);
				testWindow2.loadFile("Help.txt");
			}*/
			
			if(Keybindings.CLOSE.pressed())
			{
				OptionWindow.SelectionListener gameMenuListener = new OptionWindow.SelectionListener()
				{
					public void select(int option) 
					{
						try
						{
							switch (option)
							{
							case 1:
								break;
								
							case 2:
								manager.saveGame(manager.getProfileName()+".root.savegame", true);
								break;
							
							case 3:
								manager.saveGame(manager.getProfileName()+".root.savegame", true);
								setupMainMenu();
								break;							
							
							case 4:
								setupMainMenu();
								break;
							}
						}
						catch (IOException ex)
						{
							ex.printStackTrace();
						}
					}
				};
				OptionWindow menu = new OptionWindow(0, 0, screenWidth, screenHeight, "Game Menu", gameMenuListener);
				menu.addOption("Continue");
				menu.addOption("Save");
				menu.addOption("Save and Quit");
				menu.addOption("Quit without saving");
			}
		}
		catch (Exception ex) 
		{
			ex.printStackTrace();
		}
	}
	
	public void handleLoop()
	{
		windows.onLoop();
		camera.onLoop();
		if (renderer != null)
		{
			int x = camera.getX() - camera.getOffsetX();
			int y = camera.getY() - camera.getOffsetY();
			
			renderer.setCameraLock(x, y);
		}
	}
	
	public void handleDraws()
	{
		if (windows.getNumberOfWindows() > 0)
		{
			interfaceScreen.fill(' ');
			windows.draw(interfaceScreen);
			interfaceDrawer.draw(interfaceScreen);
		}
		else
		{
			CharImage ci = new CharImage(3, 1);
			ci.setImage("...");
			
			interfaceDrawer.draw(ci);
		}
	}
	
	public void handleExit()
	{
		System.exit(0);
	}

	private boolean checkNPCcollision(int x, int y)
	{
		List<MapObject> objects = map.getObjectsAt(x, y);
		for (MapObject mo: objects)
		{
			if (mo instanceof NPC)
			{
				return true;
			}
		}
		return false;
	}
	
	//getters
	public int getScreenWidth()
	{
		return screenWidth;
	}
	public int getScreenHeight()
	{
		return screenHeight;
	}
	public WindowHandler getWindows() 
	{
		return windows;
	}
	public Map getMap() 
	{
		return map;
	}
	public Player getPlayer() 
	{
		return player;
	}
	public PlayerAvatar getAvatar() 
	{
		return avatar;
	}

	//setters
	public void setMap(Map map)
	{
		this.map = map;
	}
	public void setPlayer(Player player)
	{
		this.player = player;
	}
	public void setAvatar(PlayerAvatar avatar)
	{
		this.avatar = avatar;
		
		camera = new Camera(5, 5);
		camera.lock(avatar);
		camera.setOffsetX(-screenWidth/2);
		camera.setOffsetY(-screenHeight/2);
		map.addMapObject(avatar);
		avatar.setMap(map);
	}
	public void setRenderer(WorldRenderer renderer)
	{
		this.renderer = renderer;
		renderer.setMap(map.getImage().toString());
	}
	
	public BattleWindow.BattleListener randomEncounter = new BattleWindow.BattleListener() 
	{	
		@Override
		public void victorious(BattleReward reward) 
		{
			getPlayer().gainExp(reward.getExp());
			getPlayer().getInventory().getGoldInv().amount += reward.getGold();
			
			for (Item item: reward.getLoot())
			{
				getPlayer().getInventory().receiveItem(item);
			}
			for (String enem: reward.getMonsterNames())
			{
				for (Mission mission: getPlayer().getMissions())
				{
					mission.killMonster(enem, getPlayer());
				}
			}
			
			TextWindow rewardWindow = new TextWindow(0, 0, getScreenWidth(), getScreenHeight(), 1, false);
			rewardWindow.addParagraph(reward.toString());
		}
		
		@Override
		public void defeated() 
		{
			setupMainMenu();
		}
	};
}
